package com.code.learn.Ipersistance.sqlSession;

import com.code.learn.Ipersistance.config.Configuration;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Proxy;
import java.lang.reflect.Type;
import java.util.List;

/**
 * @author Nina
 * @create 2020/12/7 下午4:16
 * 本类还不是真正的执行类，真正的执行类又添加了一层封装，到Executor中
 */
public class DefaultSqlSession implements SqlSession{
    private Configuration configuration;

    public DefaultSqlSession(Configuration configuration) {
        this.configuration = configuration;
    }


    @Override
    public <E> List<E> selectList(String statementId, Object... params) throws Exception {
        // 真正的执行JDBC封装在Executor里
        Executor executor = new SimpleExecutor(configuration);

        return executor.query(statementId, params);
    }

    /**
     * getMapper是使用JDK动态代理为Dao接口生成代理对象，将代理对象返回
     * 注意： 这个返回值类型包容List<Object>
     * @param mapperClass
     * @param <T>
     * @return
     */
    @Override
    public <T> T getMapper(Class<?> mapperClass) {
        // JDK动态代理三个参数：定义代理类的Class，当前相当于本类；要实现的所有的Class数组，当前只有IUserDao.class==mapperClass，还有InvocationHandler
        Object proxyInstance = Proxy.newProxyInstance(DefaultSqlSession.class.getClassLoader(), new Class[] {mapperClass}, new InvocationHandler() {
            // 所有调用getMapper执行操作之前都会先调用invoke方法
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                // 这里就是根据不同情况，来调用selctList或者selectOne
                // 返回值类型
                Type genericParameterType = method.getGenericReturnType();
                // 根据返回值类型是否 泛型类型参数化，判断调用方法
                if (genericParameterType instanceof ParameterizedType){
                    List<Object> objects = selectList(parseStatementId(method), args);
                    return objects;
                } else if ("void".equals(genericParameterType.getTypeName())){
                    switch (method.getName()){
                        case "add":
                            add(parseStatementId(method), args);
                            break;
                        case "modify":
                            modify(parseStatementId(method), args);
                            break;
                        case "remove":
                            remove(parseStatementId(method), args);
                            break;
                    }
                    return null;
                } else {
                    return selectOne(parseStatementId(method), args);
                }
            }

        });
        return (T)proxyInstance;
    }

    private void add(String statementId, Object... args) throws Exception {
        Executor executor = new SimpleExecutor(configuration);
        executor.update(statementId, args);
    }

    private void remove(String statementId, Object... args) throws Exception{
        Executor executor = new SimpleExecutor(configuration);
        executor.update(statementId, args);
    }

    private void modify(String statementId, Object... args) throws Exception{
        Executor executor = new SimpleExecutor(configuration);
        executor.update(statementId, args);
    }

    /**
     * 所以这里对getMapper()方法有要求：
     * 方法名必须和<select>标签的id一致
     * 方法所在的Dao接口路径必须和namespace一致
     * 方法的返回值必须和resultType一致
     * 方法的参数必须和parameterType一致
     * @param method
     * @return
     */
    private String parseStatementId(Method method){
        // 方法名称
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();
        return className+"."+methodName;
    }


    @Override
    public Object selectOne(String statementId, Object... params) throws Exception {
        Executor executor = new SimpleExecutor(configuration);
        return executor.queryOne(statementId,params);
    }
}
